
package hasthi.container.p2p;

import hasthi.common.HasthiException;
import hasthi.common.Utils;
import hasthi.common.XmlBeanUtils;
import hasthi.common.HasthiException.FaultCode;
import hasthi.common.constants.HasthiConstants;
import hasthi.container.ContainerUtils;
import hasthi.manager.BroadcastDocument.Broadcast;

import java.io.IOException;
import java.io.Reader;
import java.util.Hashtable;
import java.util.Map;
import java.util.UUID;

import javax.xml.namespace.QName;

import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlObject;
import org.w3.x2003.x05.soapEnvelope.EnvelopeDocument;
import org.w3.x2005.x08.addressing.ActionDocument;
import org.w3.x2005.x08.addressing.AttributedURIType;
import org.w3.x2005.x08.addressing.EndpointReferenceType;
import org.w3.x2005.x08.addressing.FromDocument;
import org.w3.x2005.x08.addressing.MessageIDDocument;
import org.w3.x2005.x08.addressing.RelatesToType;
import org.w3.x2005.x08.addressing.ReplyToDocument;

import rice.p2p.commonapi.Id;

public class XmlMesssage {
    protected EnvelopeDocument envelope;
    protected Map<QName,Object> headers;
    
    public XmlMesssage(){}
    
    public XmlMesssage(EnvelopeDocument envelope){
        this.envelope = envelope;
        XmlCursor c = envelope.getEnvelope().getHeader().newCursor();
        headers = XmlBeanUtils.extractProperties(c,true);
        c.dispose();
    }
    
    
    public XmlMesssage(XmlObject message,Id senderID,String soapAction){
        envelope = EnvelopeDocument.Factory.newInstance();
        headers = new Hashtable<QName, Object>();
        
        XmlObject body = envelope.addNewEnvelope().addNewBody();
        envelope.getEnvelope().addNewHeader();
        
        FromDocument fromDocument = FromDocument.Factory.newInstance();
        String senderidAsStr = senderID.toStringFull();
        
        
        fromDocument.addNewFrom().addNewAddress().setStringValue(senderidAsStr);
        addHeader(fromDocument);
        
        MessageIDDocument messageIDDocument = MessageIDDocument.Factory.newInstance();
        messageIDDocument.addNewMessageID().setStringValue(Utils.createUUID());
        addHeader(messageIDDocument);
        
        ActionDocument actionDocument = ActionDocument.Factory.newInstance();
        actionDocument.addNewAction().setStringValue(soapAction);
        addHeader(actionDocument);
        
        ReplyToDocument replyToDocument = ReplyToDocument.Factory.newInstance();
        replyToDocument.addNewReplyTo().addNewAddress().setStringValue(senderidAsStr);
        addHeader(replyToDocument);
        
        XmlBeanUtils.addElementAsChild(body, message);
    }
    
    
    @Override
    public String toString() {
        return XmlBeanUtils.prettyPrint(envelope);
    }

    public XmlObject getEnvelope() {
        return envelope;
    }
    
    public XmlObject getBodyContent() throws HasthiException {
        return XmlBeanUtils.extractBodyContent(envelope);
    }
    
    public void addHeader(XmlObject header){
        XmlBeanUtils.addElementAsChild(envelope.getEnvelope().getHeader(), header);
        XmlCursor c = header.newCursor();
        headers.put(XmlBeanUtils.getElementName(header),XmlBeanUtils.readValueOffXml(c,true));
        c.dispose();
    }

    /**
     * returns the content of header, not the header itself
     * @param name
     * @return
     * @throws HasthiException
     */
    public Object getHeader(QName name){
        if(headers == null){
            headers = new Hashtable<QName, Object>();
        }
        return headers.get(name);
    }
    
    
    public static String findURIValue(Object actionObj){
        if(actionObj instanceof String){
            return (String)actionObj;
        }else{
            AttributedURIType uriType = (AttributedURIType)actionObj;
            if(uriType != null){
                return uriType.getStringValue();    
            }else{
                return null;
            }
            
        }
    }
    
    public String getAction(){
        Object actionObj = getHeader(ContainerUtils.WSA_ACTION);
        return findURIValue(actionObj);
    }
    
    public String getTo(){
        Object header = getHeader(ContainerUtils.WSA_TO);
        if(header instanceof EndpointReferenceType){
            EndpointReferenceType epr = (EndpointReferenceType)header;
            return epr.getAddress().getStringValue();
        }else{
            return findURIValue(header);
        }
        
    }

    public String getReplyTo(){
        Object header = getHeader(ContainerUtils.WSA_REPLY_TO);
        if(header == null){
            return null;
        }else if(header instanceof EndpointReferenceType){
            EndpointReferenceType epr = (EndpointReferenceType)header;
            return epr.getAddress().getStringValue();    
        }else{
            AttributedURIType epr = (AttributedURIType)header;
            return epr.getStringValue();
        }
        
    }

    public String getMessageID() {
        return findURIValue(getHeader(ContainerUtils.WSA_MESSAGEID));
    }
    
    public Broadcast getBroadcast(){
        return (Broadcast) decendentByName(HasthiConstants.BROADCAST_HEADER);
    }
    
    
    public String getRelatesTo(){
        Object relatesToObj = getHeader(ContainerUtils.WSA_RELATES_TO);
        if(relatesToObj == null){
            return null;
        }else if(relatesToObj instanceof RelatesToType){
            RelatesToType uri = (RelatesToType)relatesToObj;
            if(uri != null){
                return uri.getStringValue();   
            }else{
                return null;
            }   
        }else{
            return (String)relatesToObj;
        }
    }
    
    public XmlObject decendentByName(QName name){
        return XmlBeanUtils.findByXpath(name, envelope);
    }
    
}

